/*
 * Copyright (C) 2012 praetorian droid
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef COMMON_H
#define COMMON_H

#include <android/log.h>

#include <GLES/gl.h>

#define LOG_TAG "arkanoid-jni"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

typedef long long mtime_t;

const int CT_PLATFORM_TOUCH  = 0;
const int CT_WALL_TOUCH      = 1;
const int CT_FLOOR_COLLISION = 2;
const int CT_BRICK_COLLISION = 3;

const float BALL_RADIUS    = 1.f / 80;
const float WALL_THICKNESS = 1.f / 160;
const float PLATFORM_WIDTH = .5f; // Quarter of the screen width

typedef void (*collision_handler_t) (int);

/*
 * NOTE Coordinate system for all internal calculations is:
 *
 *             ^ Y
 *             |
 *     +-------+--- maxY
 *     |       |
 *     |-1     |       |1  X
 *    -+-------+-------+-->
 *     |       |       |
 *     |       |
 *     +-------+--- -maxY
 *             |
 *
 *
 * All angles are from Y axis to X:
 *
 * (top    right quarter)  ->  a=[    0;  Pi/2]
 * (bottom right quarter)  ->  a=[ Pi/2;    Pi]
 * (top     left quarter)  ->  a=[-Pi/2;     0]
 * (bottom  left quarter)  ->  a=[  -Pi; -Pi/2]
 */

struct Vector {
    float direction; // 0 - up, Pi/-Pi - down, Pi/2 - right, -Pi/2 - left
    float length;    // depends on the vector type
};

class GLScreen {
public:
    GLScreen(int width, int height);
   ~GLScreen();
    void draw();
    int getWidth()   const { return mWidth; }
    int getHeight()  const { return mHeight; }
    float getRatio() const { return mRatio; }
private:
    int mWidth, mHeight;
    float mRatio;
};

class GLObject {
public:
    virtual void setup(const GLScreen &screen) = 0;
    virtual void draw() = 0;
};

class Brick: public GLObject {
public:
    void setup(const GLScreen &screen);
    void draw();
    // left bottom corner
    float getX1() const { return mVertices[0]; }
    float getY1() const { return mVertices[1]; }
    // right top corner
    float getX2() const { return mVertices[4]; }
    float getY2() const { return mVertices[3]; }
private:
    GLfloat mVertices[12];
};

class Ball: public GLObject {
public:
    Ball(const Brick &brick): x(0), y(0), mBrick(brick), mRunning(false), mCollisionHandler(NULL) {
        mMomentum.direction = 0;
        mMomentum.length = 0;
        mAcceleration.direction = 0;
        mAcceleration.length = 0;
    }
    void setup(const GLScreen &screen);
    void draw();
    void run();
    void stop();
    void onTiltChanged(float newTilt); // Takes gravity direction (see coordinates definition above)
    void setCollisionHandler(collision_handler_t handler) { mCollisionHandler = handler; }
private:
    void move(float dt);
    bool gotTheBrick();

    const Brick &mBrick;
    float x, y, maxY;
    bool mRunning;
    mtime_t mPrevT;
    Vector mMomentum;
    Vector mAcceleration;
    collision_handler_t mCollisionHandler;
};

class Wall: public GLObject {
public:
    void setup(const GLScreen &screen);
    void draw();
private:
    GLfloat mVertices[8];
};

class Platform: public GLObject {
public:
    void setup(const GLScreen &screen);
    void draw();
private:
    GLfloat mVertices[4];
};

#endif
